package AbstractMapper;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Hashtable;


import org.opencyc.api.CycAccess;
import org.opencyc.api.CycApiException;
import org.opencyc.cycobject.CycConstant;
import org.opencyc.cycobject.CycList;
import org.opencyc.cycobject.CycObject;
import org.opencyc.cycobject.CycVariable;
import org.opencyc.util.Log;

/**
 * Handles connection to OpenCyc - throws exceptions where appropriate or deals with them by returning null
 * 
 * @author Nic Di Noia - nik.ddn@gmail.com
 */
public class CycConnection {

	
	/**
	 * the CycAccess object
	 */
	private CycAccess cycAccess;
	/**
	 * Initializes the analogy with the proper sort index.
	 */
	protected int sortBy = 0; //0 for votingMethod, 1 for totalMatch, 2 for inverseDistance, 3 for firstDistance, 4 for zScore, 5 for angle
	protected int expandBy = 0; // 0 for none, 1 for genls, 2 for isas, 3 for all, 4 for all plus original constants
	protected int requiredScore = 1; // 0 for all scores, 1 for sorting score only (same-similarity type only if voting method selected)
	protected Hashtable<String, AbstractElementPair> allPairs;
	static final int totalVoters = 5; //number of scoring types
	protected boolean scoresCalcd = false;
	protected boolean toggleVerb = true;
	
	/**
	 * Connection to OpenCyc
	 */
	public CycConnection() {
		System.out.println("Initializing Cyc server connection, and caching frequently used terms.");
		try {
			cycAccess = new CycAccess();
		}
		catch (Exception e) {
			Log.current.errorPrintln(e.getMessage());
			Log.current.printStackTrace(e);
		}
		this.toggleVerbose();
		//cycAccess.traceOn();
		//Log.current.println("Now tracing Cyc server messages");
		System.out.println("Connection ready.");
	}
	public void toggleVerbose()
	{
		if (toggleVerb == false)
		{
			cycAccess.traceOn();
			toggleVerb = true;
		}
		else
		{
			cycAccess.traceOff();
			toggleVerb = false;
		}
	}
	
	public void setAllPairs(Hashtable<String, AbstractElementPair> allPairs) {
		this.allPairs = new Hashtable<String, AbstractElementPair>(allPairs);
		
	}
	public boolean calcAllPairScores()
	{
		if ((this.allPairs!=null) && (!this.allPairs.isEmpty()))
		{			
		
			boolean ret = false;	
			for (Enumeration<AbstractElementPair> e = this.allPairs.elements() ; e.hasMoreElements() ;) 
			{			
				AbstractElementPair elem = e.nextElement();
				if (this.getRequiredScore()==0)//calc all scores
					ret = elem.calcCatSimScores() && elem.calcAngle();
				else if  (this.getRequiredScore()>=1)//calc only same similarity-type scores as selected voter
				{
					if (this.getSortBy()< 5)
						ret = elem.calcCatSimScores();
					else
						ret =elem.calcAngle();
				}
			}
			this.scoresCalcd = ret;
		return ret;
		}
		else return false;
	}
	/**
	 * Creates a new permanent constant in the connected OpenCyc instance.
	 * @param constantName The String name of the new constant to create. 
	 * @return The CycConstant that has been created.
	 * @throws UnknownHostException if cyc server host not found on the network
   	 * @throws IOException if a data communication error occurs
   	 * @throws CycApiException if the api request results in a cyc server error
	 */
	protected CycConstant createNewPermanent(String constantName)
	{ 
		try {
			return cycAccess.createNewPermanent(constantName);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CycApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * Removes a constant from the connected OpenCyc DB.
	 * @param constant The CycConstant to remove from OpenCyc 
	 */
	protected void kill(CycConstant constant)
	{
		try {
			cycAccess.kill(constant);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/** 
	 * @param newSortBy the index that selects what scoring method should be used for sorting.
	 * @return the index that is currently set for sorting.
	 */
	public int setSortBy(int newSortBy)
	{
		this.sortBy = newSortBy;
		return this.sortBy;
	}
	
	public int setExpandBy(int newExpandBy)
	{
		this.expandBy = newExpandBy;
		return this.expandBy;
	}
	 /**
	  * 
	  * @param newRequired  0 for all scores, 1 for sorting score, 2 for only attrib scores
	  * @return
	  */
	public int setRequiredScore(int newRequired) {
		this.requiredScore = newRequired;
		return this.requiredScore;
	}
	
	/**
	 * @return the index that is currently set for sorting.
	 */
	public int getSortBy()
	{return this.sortBy;	}
	/**
	 * 
	 * @return 0 for none, 1 for genls, 2 for isas, 3 for all, 4 for all plus original constants
	 */
	public int getExpandBy()
	{return this.expandBy;	}
	
	public int getRequiredScore() {	
		return this.requiredScore;
	}
	
	/**
	 * Reports what score each sort index is sorting by.
	 * @param sortNum The index number to sort by.
	 * @return The string representation of the index number for sorting.
	 */
	public static String reportSortBy(int sortNum)
	{
		String result = new String();						
		switch (sortNum)
		{
		case 0:
			result+= "Voting method";
			break;
		case 1:						
			result+= "Total Hits";
			break;
		case 2:
			result+= "Inverse Distance";
			break;
		case 3:		
			result+= "First Distance";
			break;
		case 4:		
			result+= "Z-Score";
			break;
		case 5:		
			result+= "Vector Space Model";
			break;
		}				
		return result;
	}

	public static String reportExpandBy(int expandNum)
	{
		String result = new String();						
		switch (expandNum)
		{
		case 0:
			result+= "No Expansion";
			break;
		case 1:						
			result+= "Replace cycConstant with its generalizations";
			break;
		case 2:
			result+= "Replace cycConstant with its instantiations";
			break;
		case 3:		
			result+= "Replace cycConstant with its generalizations and instantiations";
			break;
		case 4:		
			result+= "Add each cycConstant's generalizations and instantiations";
			break;
		}				
		return result;
	}
	public static String reportRequiredScore(int index) {
		String result = new String();						
		switch (index)
		{
		case 0:
			result+= "Require all scores (voting method will include vector angles)."; //voting method here includes all scores, angles too
			break;
		case 1:						
			result+= "Require sorting score only (voting method will not include vector angles).";//voting method here does not include angles
			break;
		}				
		return result;
	}

	public static void checkConstant(String userInput) throws InvalidCycConstantException
	{
		userInput = userInput.trim();
		for (int i =0; i < userInput.length(); i++)
		{
			Character c = new Character(userInput.charAt(i));
			int ci = c;
			if( (ci < 45) ||  (ci == 47) ||  ((ci >= 58) && (ci<=64)) || ((ci >= 91) && (ci<=96))) 
			{
				throw new InvalidCycConstantException();			
			}
			//log approved constant
		}
	}		
	
	/**
	 * Request OpenCyc to find a constant.
	 * 
	 * @param constantName the String name of the constant to search for.
	 * @return The cycConstant found or null if none were found.
	 * @throws UnknownHostException if cyc server host not found on the network
	 * @throws IOException if a data communication error occurs
	 * @throws CycApiException if the api request results in a cyc server error
	 * @see org.opencyc.api.cycAccess#find
	 */
	protected CycConstant findConstant(String constantName)
	{ 
		try {
			return cycAccess.find(constantName);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CycApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * Asks OpenCyc to find a constant with different letter-case spelling.
	 * @param constantName the String name of the constant to search for.
	 * @return The cycConstant found or null if none were found.
	 * @throws UnknownHostException if cyc server host not found on the network
	 * @throws IOException if a data communication error occurs
	 * @throws CycApiException if the api request results in a cyc server error
	 * @see org.opencyc.api.cycAccess#constantNameCaseCollision
	 */
	protected CycConstant findCaseCollition(String constantName)
	{ 
		try {
			return cycAccess.constantNameCaseCollision(constantName);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CycApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * Ask OpenCyc to find constants that contain the given string in their denotations. 
	 * @param constantName the String name of the constant to search for.
	 * @return The list of constants found or null if none were found.
	 * @throws UnknownHostException if cyc server host not found on the network
	 * @throws IOException if a data communication error occurs
	 * @throws CycApiException if the api request results in a cyc server error
	 */
	protected CycList getDenotsOfString(String constantName)
	{		
		try {
			return cycAccess.getDenotsOfString(constantName);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CycApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		return null;
	}	  	  
	/**
	 * Ask OpenCyc to get the comments of a given constant. 
	 * @param constantName the constant to get comments for.
	 * @return The String found or an empty string if no comments were found.
	 * @throws UnknownHostException if cyc server host not found on the network
	 * @throws IOException if a data communication error occurs
	 * @throws CycApiException if the api request results in a cyc server error
	 */
	protected String getComment(CycConstant constantName)
	{
		try {
			return cycAccess.getComment(constantName);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CycApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}	  
	/**
	 * Ask OpenCyc for a list of all instance relationships which contain a given constant.
	 * @param constantName The constant to be found in the instance relationships.
	 * @return the CycList of all constants which are instances of the given constant.
	 */
	protected CycList getAllIsa(CycConstant constantName)
	{		  
		try {
			return cycAccess.getAllIsa(constantName);
		} catch (UnknownHostException e) {
			e.printStackTrace();
			return null;
		} catch (CycApiException e) {			
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}		  
	/**
	 * Ask OpenCyc for a list of all generalization predicate relationships which contain a given constant.
	 * @param constantName The constant to be found in the generalization predicate relationships.
	 * @return the CycList of all constants which are generalization predicates of the given constant.
	 */
	protected CycList getAllGenlPreds(CycConstant constantName)
	{
		try {
			return cycAccess.getAllGenlPreds(constantName);
		} catch (UnknownHostException e) {
			e.printStackTrace();
			return null;
		} catch (CycApiException e) {			
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}	  
	/**
	 * Ask OpenCyc for a list of all generalization relationships which contain a given constant.
	 * @param constantName The constant to be found in the generalization relationships.
	 * @return the CycList of all constants which are generalizations of the given constant.
	 */
	protected CycList getAllGenls(CycConstant constantName)
	{
		try {
			return cycAccess.getAllGenls(constantName);
		} catch (UnknownHostException e) {			
			e.printStackTrace();
			return null;
		} catch (CycApiException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}	  
	/**
	 * Ask OpenCyc for a list of all dependent specialization relationships which contain a given constant.
	 * @param constantName The constant to be found in the dependent specialization relationships.
	 * @return the CycList of all constants which are dependent specializations of the given constant.
	 */
	protected CycList getDepSpecs(CycConstant constantName)
	{
		try {
			return cycAccess.getAllDependentSpecs(constantName);
		} catch (UnknownHostException e) {			
			e.printStackTrace();
			return null;
		} catch (CycApiException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	/**
	 * Ask OpenCyc for a list of all specialized inverse relationships which contain a given constant.
	 * @param constantName The constant to be found in the specialized inverse relationships.
	 * @return the CycList of all constants which are specialized inverse of the given constant.
	 */
	protected CycList getSpecInvs(CycConstant constantName)
	{
		try {
			return cycAccess.getAllSpecInverses(constantName);
		} catch (UnknownHostException e) {
			e.printStackTrace();
			return null;
		} catch (CycApiException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}	
	//TODO document
	protected CycList getWhyCollectionsIntersect(CycConstant collection1, CycConstant collection2)
	{
		try {
			return cycAccess.getWhyCollectionsIntersect(collection1, collection2);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
		
	}
	//TODO document	
	protected CycList getAllAssertionsInMt(CycConstant mt)
	{
		try {
			return cycAccess.getAllAssertionsInMt(mt);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	/**
	 * Creates a CycList out of a String - Used to create command chains for OpenCyc
	 * @param constantName the String to be made into a CycList
	 * @return the CycList object made out of the string constant
	 */
	protected CycList makeCycList(String constantName)
	{
		return cycAccess.makeCycList(constantName);
	}	 
	/**
	 * Asks cyc to assert a formula into the OpenCyc world.
	 * @param formula The formula to be asserted - usually created with {@link #makeCycList(String)}
	 * @return True if the assertion was accepted by the OpenCyc connection, false otherwise.
	 */
	protected boolean assertFormula(CycList formula)
	{
		try {
			cycAccess.assertWithTranscriptAndBookkeeping(formula, CycAccess.universalVocabularyMt);
		} catch (UnknownHostException e) {			
			//e.printStackTrace();
			return false;
		} catch (CycApiException e) {
			//e.printStackTrace();
			return false;
		} catch (IOException e) {
			//e.printStackTrace();
			return false;
		}
		return true;
	}
	/**
	 * TODO CONTINUE DOCUMENTING HERE
	 * @param constantName
	 * @return
	 */
	protected CycList getRecursiveALL(String constantName)
	{		  
		CycConstant theTerm;
		try {
			theTerm = cycAccess.getKnownConstantByName(constantName);
			CycList ALL = cycAccess.getAllDependentSpecs(theTerm);
			ALL.addAll(cycAccess.getAllGenls(theTerm));
			ALL.addAll(cycAccess.getAllGenlPreds(theTerm));			
			ALL.addAll(cycAccess.getAllIsa(theTerm));
			ALL.addAll(cycAccess.getAllSpecInverses(theTerm));
			return ALL;
		} catch (UnknownHostException e) {			
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}		    
		return null;
	}

	protected CycConstant getConstant(String constantName)
	{		    
		try {
			System.out.println("Getting known constant by name: " + constantName + "\n");
			CycConstant answer = cycAccess.getKnownConstantByName(constantName);
			System.out.println("\nThe obtained constant is " + answer.cyclify());
			return answer;
		} catch (UnknownHostException e) {				
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;

	}	  	 

	protected CycList getGeneralizations(CycConstant constant)
	{

		try {
			System.out.println("Getting generalizations of: "+ constant.cyclify() + "\n");
			CycList genls = cycAccess.getGenls(constant);
			System.out.println("\nThe obtained direct genls are:\n" + genls.cyclify());
			return genls;
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	protected CycList getIsa(CycConstant constant)
	{		 
		try {
			System.out.println("Getting generalizations of: "+ constant.cyclify() + "\n");
			CycList isas = cycAccess.getIsas(constant);
			System.out.println("\nThe obtained direct genls are:\n" + isas.cyclify());
			return isas;
		} catch (UnknownHostException e) {
			e.printStackTrace();			
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	protected CycList queryVariable(CycList query, CycVariable variable, CycObject mt)
	{
		try {
			CycList bindings;
			bindings = cycAccess.queryVariable(variable,query, mt,null);
//			System.out.println("ANSWER TO QUERY: "+ bindings);
	//		System.out.println("Size of answer: "+ bindings.size());
		//	System.out.println("QUERY well formed: "+ cycAccess.isWellFormedFormula(query));

			return bindings;
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
		
	}
	protected CycList queryVariables(CycList query, CycList variables, CycObject mt)
	{
		try {
			CycList bindings;
			bindings = cycAccess.queryVariables(query, variables, mt, null);
				
//			System.out.println("ANSWER TO QUERY: "+ bindings);
	//		System.out.println("Size of answer: "+ bindings.size());
		//	System.out.println("QUERY well formed: "+ cycAccess.isWellFormedFormula(query));
			return bindings;
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
		
	}	
}
